Redis ACL
AUTH命令变化及ACL规则
Redis ACL是对每个新连接的每个命令和key做了限制,所以使用老客户端连接启用了ACL的Redis也能兼容。ACL区分用户名,如果使用了旧版本的Redis-client,则相当于使用了“default”用户。
新版本的认证命令:
# Redis 6+ 使用<username>指定的用户
AUTH <username> <password>
# Redis 6- 使用default用户
AUTH <password>
因为Redis6继承了旧版本的AUTH命令,所以如果只传一个参数,相当于用“default”用户登录。
# 获取服务器上当前已激活的ACL规则,每个用户占一行
ACL LIST
默认情况下只有default一个用户,如果不要求AUTH,则返回如下结果
# "user <username> <isEnabled> <password> <key> <channel> <command>"
1) "user default on nopass ~* &* +@all"
其中:
内容 | 含义 |
---|---|
user default | "default"用户 |
on | 用户已激活 |
nopass | 不需要密码进行鉴权 |
~* | 可以访问所有的key |
&* | 发布、订阅channel |
+@all | 可以执行所有命令 |
参数 | 可选值 | 说明 |
---|---|---|
isEnabled | on off | on表示激活该用户,可以使用该用户进行认证。 off表示关闭该用户,已连接的用户不受影响。如果off了default用户,服务端会要求客户端发送AUTH或者HELLO命令进行认证。 |
command | +<command> -<command> +@<category> -@<category> +<command>|subcommand allcommands nocommands | +<command>: 可以使用该命令 -<command>: 不可以使用该命令 +@<category>: 允许用户执行一类命令(ACL CAT命令可查看完整的类型列表) -@<category>: 不允许用户执行一类操作 +<command>|subcommand: 当<command>命令被禁用时,允许用户运行subcommand命令,如果command命令整个被启用了,该ACL规则会报错 allcommands: +@all的别名 nocommands:-@all的别名 |
key | ~<pattern> allkeys resetkeys | ~<pattern>: 允许访问该glob-style模式匹配到的key allkeys: ~*的别名 resetkeys: 清除已分配的key patterns.如 ~foo:* ~bar:* resetkeys ~objects:* ,则只有~objects:*会生效 |
channel | &<pattern> allchannels resetchannels | &<pattern>: 用户可以访问该glob-style模式匹配到的channel allchannels: 允许访问所有的channel resetchannels: 清除所有允许的channel模式,并且断开无访问权限的客户端 |
password | ><password> <<password> #<hash> !<hash> nopass resetpass reset | ><password>: 给用户添加密码。该指令会删掉nopass指令。每个用户可以有多个密码。 <<password>: 删除用户的一个密码,如果删除的密码是该用户未使用的,则会报错。 #<hash>: 以SHA-256散列值的形式为用户添加密码。用户可以在acl.conf文件中存储SHA256散列值而不是明文密码。散列值必须是64位小写十六进制字符串。 !<hash>: 从用户密码列表中删除指定hash。当不确定是否有指定密码时,可以用该指令。 nopass: 所有的密码都会被删除,用户也会被标记为不需要密码,resetpass指令会清除这个标记。 resetpass: 清除所有密码和nopass标记。resetpass之后,用户没有分配的密码,且必须认证。(相当于无法登录) reset: 相当于给了resetpass、resetkeys、resetchannels、off、-@all,用户立即变为该状态 |
创建和修改ACL
有两种方式可以创建和修改ACL:
- 使用ACL命令和ACL SETUSER子命令;
- 修改服务端配置,或者使用ACL LOAD子命令加载外部ACL文件。
使用SETUSER子命令
# 语法
ACL SETUSER <username>
# 示例
ACL SETUSER alice
SETUSER子命令用于给用户应用ACL规则,如果像上例中那样没指定规则,将会视用户是否存在而有不同的表现:如果用户不存在,则会使用默认规则创建一个用户;如果用户存在,就不做任何修改。
默认情况下创建新用户会被严格限制权限。上例中如果创建了alice用户,通过"ACL LIST"子命令查看,会返回"user alice off &* -@all",意味着该用户会处于off状态,无密码,不能使用任何命令,不能访问任何key,但是可以访问所有的发布订阅channel。
发布订阅Channel的ACL访问控制是在Redis 6.2开始出现的,所以为了向后兼容,新用户默认情况下都会被授予"allchannels"权限。如果需要更改默认的channel权限,可以设置redis.conf文件中的acl-pubsub-default的值为resetchannels。
上例中创建的用户完全无用,可通过下面这个例子,激活alice用户,并设置密码,只允许使用GET命令,访问"cached:"开头的key。
ACL SETUSER alice on >p1pp0 ~cached:* +get
查看用户的ACL规则,除了"ACL LIST"子命令外,还可以使用"ACL GETUSER"子命令。
# 语法
ACL GETUSER <USERNAME>
# 示例
ACL GETUSER alice
返回结果示例:
# 使用Redis默认协议version 2的返回内容
> ACL GETUSER alice
1) "flags"
2) 1) "on"
2) "allchannels"
3) "passwords"
4) 1) "2d9c75..."
5) "commands"
6) "-@all +get"
7) "keys"
8) 1) "cached:*"
9) "channels"
10) 1) "*"
# 使用RESP3协议的返回内容
> ACL GETUSER alice
1# "flags" => 1~ "on"
2~ "allchannels"
2# "passwords" => 1) "2d9c75273d72b32df726fb545c8a4edc719f0a95a6fd993950b10c474ad9c927"
3# "commands" => "-@all +get"
4# "keys" => 1) "cached:*"
5# "channels" => 1) "*"
多次调用SETUSER子命令
每次调用SETUSER子命令,都不会重置用户的权限,只会增量的给用户授权。举个例子:
> ACL SETUSER myuser +set
OK
> ACL SETUSER myuser +get
OK
两次SETUSER后,结果是既有set命令的执行权限,也有get命令的执行权限。
> ACL LIST
1) "user default on nopass ~* &* +@all"
2) "user myuser off &* -@all +set +get"
使用命令类型
> ACL SETUSER antirez on +@all -@dangerous >42a979... ~*
上例会给antirez用户分配所有命令的执行权限,在此基础上移除dangerous类型下的命令。
除了+@all以外,其他命令类型都不会包含modules中的命令。
# 列出所有可用的命令类型
ACL CAT
# 列出某一类型下的所有命令
ACL CAT <category-name>
添加子命令ACL规则
如果想禁用一个命令,但是又需要让用户执行这个命令下的某个子命令,比如"CLIENT"命令,不想让用户有执行"CLIENT KILL"的权限,但是需要有"CLIENT SETNAME"的权限,就需要添加子命令的权限。
子命令权限只能添加不能清除,并且子命令的上级命令必须处于禁用状态,否则添加时会报错。
子命令匹配会增加性能负担,负担有多重,即使使用综合测量工具也很难估算。只有在子命令的相关命令被调用的时候,才会有额外CPU消耗;执行其他命令不会有消耗。
# 正确的操作
ACL SETUSER myuser -client +client|setname +client|getname
# 报错,因为没有禁用debug命令,错误信息中已经说明了。
> ACL SETUSER default +debug|segfault
(error) ERR Error in ACL SETUSER modifier '+debug|segfault': Adding a
subcommand of a command already fully added is not allowed. Remove the
command to start. Example: -DEBUG +DEBUG|DIGEST
Redis内部如何存储密码
Redis内部使用SHA256散列值保存密码。从Redis6开始,"CONFIG GET requirepass"命令不再返回明文密码,取而代之的是散列值。
使用SHA256是为了避免存储明文,同时又能快速认证。
ACL密码只是客户端与服务器之间共享的密钥,并不用于加密数据,而且使用各种耗费时间空间的加密算法在Redis项目组来看得不偿失,他们建议使用强密码作为密码,这样即使拿到了密码的散列值,也无法解出明文,Redis提供了"ACL GENPASS"子命令用于生成强密码。
"ACL GENAPSS"默认生成256bit的为随机字符串,转换为64字节的字符串。
# 语法
ACL GENPASS <bits>
# 示例
> ACL GENPASS
"c80746d6ff1bea3382920a4bdaa49b172f2545a28532710a7301c5e2e127737e"
> ACL GENPASS 128
"0c039d94b17646de6c2d0ab436584413"
使用外部ACL文件
在Redis配置中存储用户的两种方式,且互不兼容:
- 在redis.conf文件中直接写ACL规则;
- 通过redis.conf文件中的aclfile参数指定一个外部ACL文件。 如果使用场景较为简单,可以在redis.conf文件中指定用户权限;当场景比较复杂需要定义多个用户时,强烈建议使用ACL文件。
无论是哪种方式,ACL规则语法都是相同的。
如果是在redis.conf中直接写了ACL规则,可以使用"CONFIG REWRITE"子命令将ACL规则等配置项的变更写回到redis.conf中。
如果使用的是外部ACL文件,则可以通过"ACL LOAD"和"ACL SAVE"子命令将ACL配置读写到外部文件中。
"CONFIG REWRITE"不会触发"ACL SAVE"。
Sentinel和Replicas的ACL规则
如果不想对Replicas实例和Sentinel实例裸奔,且不影响正常功能。可以使用如下最小权限原则的ACL规则。
对于Sentinel用户,允许调用master和replica实例上的以下命令:
AUTH, CLIENT, SUBSCRIBE, SCRIPT, PUBLISH, PING, INFO, MULTI, SLAVEOF, CONFIG, CLIENT, EXEC.
Sentinel用户除了发布和订阅Channel,不需要访问数据库中的任何key,所以ACL规则可以这样写:
ACL SETUSER sentinel-user on >somepassword allchannels +multi +slaveof +ping +exec +subscribe +config|rewrite +role +publish +info +client|setname +client|kill +script|kill
对于replica实例的用户,在master实例上可以使用白名单开放如下三个命令的权限:
PSYNC, REPLCONF, PING
replica实例不需要访问任何key,所以ACL规则可以这样写:
ACL setuser replica-user on >somepassword +psync +replconf +ping
在replica实例上,不需要配置master执行命令的权限,因为master在replica实例上是以root用户的身份进行认证的。
参考文档
https://redis.io/topics/aclhttps://redis.io/commands/authhttps://redis.io/commands/acl-listhttps://redis.io/commands/acl-cathttps://redis.io/commands/acl-setuserhttps://redis.io/commands/acl-loadhttps://redis.io/commands/acl-savehttps://redis.io/commands/config-rewrite